
#include "prob-guess.h"
#include <stdlib.h>
#include "cope.h"
#include <iomanip>

/*
 * Initialize with node, since we need to get all node information
 * to create a src_info_list_.
 * And the nb_info_list_ will be created dynamically, since neighbors
 * are not stable
 */
ProbGuess::ProbGuess(MobileNode * node) { 
   
    nb_info_list_ = new list<nb_info_t>;    
  
    /* create src_info_list_ for all nodes, since every node 
     * has the probability to become a source
     */
    Node * node_item = (node->nodehead_).lh_first;
    src_info_t src_info_item;
    src_info_list_ = new list<src_info_t>;
    
    do {
	src_info_item.src_ip = node_item->address();
	src_info_item.start_seq_num = 0;
	src_info_item.end_seq_num = 0;
	src_info_item.pkt_count = 0;
	src_info_list_->push_back(src_info_item);
	node_item = node_item->nextnode();
    } while (node_item != NULL );

    // prob_info_table_ = NULL;  
}

ProbGuess::~ProbGuess() {
    src_info_list_->clear();
    NB_INFO_ITER nb_info_iter = nb_info_list_->begin();
    while(nb_info_iter != nb_info_list_->end()) {
	nb_info_iter = nb_info_list_->erase(nb_info_iter);
    }
    //nb_info_list_->clear();
    delete src_info_list_;
    delete nb_info_list_;

    // prob_info_t* current;
    // prob_info_t* temp;
    // HASH_ITER(hh, prob_info_table_, current, temp) {
    // 	HASH_DEL(prob_info_table_, current);
    // 	free(current);
    // }
}

double ProbGuess::get_prob_guess(int src_ip, int nb_id) {
  //prob_info_t * result;
  //result = find_prob_guess(src_ip, nb_id);
  //prob
    prob_key_t key;
    key.nodeid = nb_id;
    key.src_ip = src_ip;
    PROB_INFO_ITER it;

    if( ( it = prob_info_table_.find(key)) != prob_info_table_.end() )
      return it->second;
    else
      return 0.0;
     //return 0.1;
    //return 1.0;
}

prob_info_t* ProbGuess::find_prob_guess(int src_ip, int nb_id) {
    // prob_key_t key;
    // key.nodeid = nb_id;
    // key.src_ip = src_ip;
    // PROB_INFO_ITER it = prob_info_table_.find(key);

    // if( it != prob_info_table_.end() )
    //   return &(*it);
  
    return NULL;
}

/* 
 * An assistant for up_prob_info, to check whether the neighbor info exist
 */
nb_info_t * ProbGuess::get_nb_info(int nb_id) {
    NB_INFO_ITER nb_info_iter = nb_info_list_->begin();
    for( ; nb_info_iter != nb_info_list_->end(); nb_info_iter++) {
	if((*nb_info_iter).nodeid == nb_id) {
	    return &(*nb_info_iter);
	}
    }
    return NULL;
}


/* 
 * 1. update the src list according to the large and small virtual queue
 * 2. update nb_id's nb_info_t, if not exist, add a new nb_info_t to the list
 * 3. get new pkt_info, and compute a new prob
 * 4. update the new prob in prob_info_table
 * 5. reset the src list 
 */
void ProbGuess::up_prob_info(int nb_id,
			     nb_head * neighbor_list,
			     VirQueue * vir_table,
			     PacketInfo * pkt_info) {
    // cout << "update prob guess for neighbor " << nb_id << endl;
    //get the neighbor list
    COPENeighbor * nb_iter; 
    
    /* 1. update src_info_list from the virtual queues */
    COPE_GET_HEAD(neighbor_list, nb_iter); 
   
    while( nb_iter != NULL){
	up_src_list(vir_table, nb_iter->getid(), 'L');
	up_src_list(vir_table, nb_iter->getid(), 'S');

	COPE_GET_NEXT(nb_iter,nb_iter);
    }

    /* 2. update nb_info_list_ */
    nb_info_t * nb_info = get_nb_info(nb_id);
    // Info of this neighbor does not exist, add it with last_report_time = 0.0
    if(nb_info == NULL ) {
	nb_info = (nb_info_t*)malloc(sizeof(nb_info_t));
	memset(nb_info, 0, sizeof(nb_info_t)); 
	nb_info->nodeid = nb_id;
	nb_info->last_report_time = CUR_TIME; // not yet initialized    
	nb_info_list_->push_back(*nb_info);
	//printf("add nb %d\n",(*nb_info).nodeid );
    } 

    SRC_INFO_ITER src_info_iter = src_info_list_->begin();
   
    int n_count;
    double old_prob;
    double new_prob;
    double cur_prob;
    double time_period;
    prob_key_t key;
    time_period = CUR_TIME - nb_info->last_report_time;
    int time_mod = (int)(time_period / T);

    for( ; src_info_iter != src_info_list_->end(); src_info_iter++ ) {
	/* 3. compute new prob */
	n_count = pkt_info->get_pkt_count(nb_id, 
					  (*src_info_iter).src_ip,
					  (*src_info_iter).start_seq_num,
					  (*src_info_iter).end_seq_num);
	old_prob = get_prob_guess((*src_info_iter).src_ip, nb_id);
		
	if((*src_info_iter).pkt_count != 0) {
	  cur_prob = (double)n_count / (double)(*src_info_iter).pkt_count;
	  // cur_prob should be <= 1
	  cur_prob = cur_prob > 1? 1: cur_prob;	  
	} else {
	    cur_prob = 0;
	}
	//first time to initialize probability, 
	if( old_prob == 0.0 ) 
	    new_prob = cur_prob;
	else
	    new_prob = (1-ALPHA) * old_prob
	      + 
	      ALPHA * (time_period/((time_mod+1)*T)) * cur_prob
	      + 
	      ALPHA * (1-(time_period/((time_mod+1)*T))) * old_prob;

	// avoid new_prob > 1
	new_prob = (new_prob > 1) ? 1: new_prob;
	  
	key.nodeid = nb_id;
	key.src_ip =  (*src_info_iter).src_ip;

	// cout << "src_ip: " << key.src_ip 
	//      << " old: " << old_prob
	//      << " current: " << cur_prob
	//      << " new: " << new_prob
	//      << " time period " << time_period
	//      << " time mode " << time_mod
	//      << " time factor " << (time_period/((time_mod+1)*T)) 
	//      << " ncount " << n_count 
	//      << " pktcount " << (*src_info_iter).pkt_count
	//      << endl;
	/* 4. update new prob */
	prob_info_table_[key] = new_prob;
	//up_prob_info_item(key, new_prob );
    }
    nb_info = get_nb_info(nb_id);
    nb_info->last_report_time = CUR_TIME; 

    /* 5. reset the src list */
    reset_src_list();
}

/* update the new prob into prob_info_table_:
 * 1. Find the item according to key
 * 2. delete this item if exist
 * 3. set the key and prob for prob_info_item
 * 4. add this item to prob_info_table_
 */
void ProbGuess::up_prob_info_item(prob_key_t key, double new_prob) {

  //prob_info_t* result = find_prob_guess(key.src_ip, key.nodeid);

  
    // result = (prob_info_t*)malloc(sizeof(prob_info_t));
    // result->key.src_ip = key.src_ip;
    // result->key.nodeid = key.nodeid;

    // result->prob = new_prob;
    // HASH_ADD(hh, prob_info_table_, key, sizeof(prob_key_t), result);
}

/* 
 * reset start/last sequence number and pkt count of each item to zero 
 */
void ProbGuess::reset_src_list() {
    SRC_INFO_ITER src_info_iter = src_info_list_->begin();
    for( ; src_info_iter != src_info_list_->end(); src_info_iter++ ) {
	(*src_info_iter).start_seq_num = 0;
	(*src_info_iter).end_seq_num = 0;
	(*src_info_iter).pkt_count = 0;
    }
}

/* 
 * update the src list according to a specific virtual queue 
 * 1. get a packet from virtual queue
 * 2. update this packet's src_ip information by up_src_item
 */
void ProbGuess::up_src_list(VirQueue * vir_table, int nb_id, char type) {
    //    return;
    list<Packet *> * pkt_q;
    // cout << "up src list" << endl; 
    // vir_table->show();
    pkt_q = vir_table->get_vir_pktq(nb_id, type);
    if(pkt_q == NULL)
	return;
    list<Packet*>::iterator it = pkt_q->begin();
        
    for ( ; it != pkt_q->end(); it++) {
	// cout << " " << HDR_CMN(*it)->uid_
	//      << " " << HDR_TCP(*it)->seqno()
	//      << " " << HDR_CMN(*it)->cope_pkt_id()
	//      << " " << HDR_CMN(*it)->cope_seq_num()
	//     //	     << setw(8) << node_->nodeid() << " -> "
	//      << " " << HDR_CMN(*it)->next_hop();
	// if(HDR_CMN(*it)->ptype() == PT_ACK)
	//     cout << setw(8) << "ack" << endl;
	// else if(HDR_COPE(*it)->ctype_ == COPE_RETX)
	//     cout << setw(8) << "crx" << endl;
	// else
	//     cout << endl;
	
	up_src_item(HDR_IP(*it)->saddr(), HDR_CMN(*it)->cope_seq_num_);
	
    }     
}

/* 
 * update the item in src_info_list_ 
 */    
void ProbGuess::up_src_item(nsaddr_t src_ip, u_int16_t seq_num) {
    SRC_INFO_ITER src_info_iter = src_info_list_->begin();
    for( ; src_info_iter != src_info_list_->end(); src_info_iter++ ) {
	if( (*src_info_iter).src_ip == src_ip ) {
	    (*src_info_iter).start_seq_num = min((*src_info_iter).start_seq_num, 
						 seq_num);
	    (*src_info_iter).end_seq_num = max((*src_info_iter).end_seq_num, 
					       seq_num);
	    (*src_info_iter).pkt_count++;
	    break;
	}
    }
}
